import { addEvent, removeEvent } from '@/utils';
import { useCallback, useEffect, useRef, useState } from 'react';
import './index.scss';

export default function Swiper({
  tabRenderFn,
  slideRenderFn,
  swiperList,
  width,
  loop,
  interval,
  duration,
  drag,
  trigger,
}) {
  const [activeIndex, setActiveIndex] = useState(0);
  const [slideX, setSlideX] = useState(loop ? -width - 9 : 0);
  const [slideTime, setSlideTime] = useState(duration);
  const indexRef = useRef(0);

  const wrapperRef = useRef();
  const startX = useRef();
  const dragX = useRef();
  const startTime = useRef();
  const timerRef = useRef();

  function isActive(index) {
    return (
      activeIndex === index ||
      (index === 0 && activeIndex === swiperList.length) ||
      (index === swiperList.length - 1 && activeIndex === -1)
    );
  }

  const handleSlide = useCallback(
    i => {
      let x;
      if (loop) {
        x = i ? (i + 1) * -width : -width - 9;
      } else {
        x = i * -width;
      }
      setSlideX(x);
      dragX.current = x;
      setActiveIndex(i);
    },
    [loop, width]
  );

  const startSwiper = useCallback(
    delay => {
      if (!loop) return;
      timerRef.current = setTimeout(() => {
        if (++indexRef.current > swiperList.length) {
          indexRef.current = 0;
          setSlideTime(0);
          startSwiper(1500);
        } else {
          setSlideTime(duration);
          startSwiper(indexRef.current === swiperList.length ? 1500 : interval);
        }
        handleSlide(indexRef.current);
      }, delay);
    },
    [duration, handleSlide, interval, loop, swiperList.length]
  );

  const clickTab = useCallback(
    index => {
      handleSlide(index);
      indexRef.current = index;
      clearInterval(timerRef.current);
      startSwiper(interval);
    },
    [handleSlide, interval, startSwiper]
  );

  const tabItem = swiperList.map((item, index) => {
    return (
      <div
        className={`h-swiper-tab-item ${isActive(index) ? 'active' : ''}`}
        key={index}
        onClick={() => trigger === 'click' && clickTab(index)}
        onMouseOver={() => trigger === 'hover' && clickTab(index)}
      >
        {tabRenderFn
          ? tabRenderFn(item, index)
          : [
              <span className="tab-name" key={`name${index}`}>
                {item.tab}
              </span>,
              <span className="circle" key={`circle${index}`}></span>,
            ]}
      </div>
    );
  });

  !slideRenderFn && (slideRenderFn = (item, index) => <img key={index} src={item.img} alt="" />);

  const swiperItem = loop
    ? [
        slideRenderFn(swiperList[swiperList.length - 1], -1),
        ...swiperList.map(slideRenderFn),
        slideRenderFn(swiperList[0], swiperList.length),
      ]
    : swiperList.map(slideRenderFn);

  useEffect(() => {
    startSwiper(interval);
    let wrapperRefValue;
    if (wrapperRef.current) {
      addEvent(wrapperRef.current, 'transitionend', move);
      wrapperRefValue = wrapperRef.current;
    }
    function move() {
      if (indexRef.current === -1) {
        setSlideTime(0);
        clickTab(swiperList.length - 1);
      } else if (indexRef.current === swiperList.length) {
        setSlideTime(0);
        clickTab(0);
      }
    }

    return () => {
      wrapperRefValue && removeEvent(wrapperRefValue, 'transitionend', move);
      timerRef.current && clearInterval(timerRef.current);
    };
  }, [clickTab, interval, startSwiper, swiperList.length]);

  function handleDragStart(e) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'default';
    if (indexRef.current === -1 || indexRef.current === swiperList.length) {
      return false;
    }

    setSlideTime(0);
    clearInterval(timerRef.current);

    startX.current = e.clientX;
    startTime.current = new Date().getTime();
    addEvent(document, 'mousemove', handleMove);
    addEvent(document, 'mouseup', handleUp);

    return false;
  }

  function handleMove(e) {
    const distance = startX.current - e.clientX;
    setSlideX(slideX - distance);
    dragX.current = slideX - distance;
  }

  function handleUp(e) {
    const distance = startX.current - e.clientX;
    const displacement = Math.abs(distance);
    const time = new Date().getTime() - startTime.current;
    const slideSpeed = displacement / time;

    // 距离大于约一半，或滑动速度快
    if (displacement >= 1175 / 2.5 || slideSpeed >= 1) {
      indexRef.current = distance > 0 ? ++indexRef.current : --indexRef.current;
    }
    handleSlide(indexRef.current);
    setSlideTime(duration);
    startSwiper(interval);
    removeEvent(document, 'mousemove', handleMove);
    removeEvent(document, 'mouseup', handleUp);
  }

  return (
    <div className="h-swiper">
      <div className="h-swiper-tab">{tabItem}</div>
      <div className="h-swiper-container">
        <div
          draggable={drag}
          className="h-swiper-wrapper"
          style={{
            transform: `translateX(${slideX}px)`,
            transitionDuration: `${slideTime}s`,
          }}
          ref={wrapperRef}
          onDragStart={e => drag && handleDragStart(e)}
        >
          {swiperItem.map(item => (
            <div className="h-swiper-slide" key={item.key} style={{ width: `${width}px` }}>
              {item}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}
